/*******************************************************************************
 * Copyright (c) 2008, 2009 Fabio Mauro - Alberto Terraneo.
 * Permission is granted to copy, distribute and/or modify this document
 * under the terms of the GNU Free Documentation License, Version 1.2
 * or any later version published by the Free Software Foundation.
 *     
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU Free Documentation License for more details.
 *     
 * You should have received a copy of the GNU Free Documentation 
 * License along with this distribution; if not, send a email to authors:
 * aterraneo <at> users.sourceforge.net
 * bigmoby09 <at> users.sourceforge.net
 *******************************************************************************/
package it.blueocean.acanto.security;

import it.blueocean.acanto.security.model.SpringSecuritySubject;
import it.blueocean.acanto.web.AcantoPlatformApplication;

import org.apache.log4j.Logger;
import org.apache.wicket.Application;
import org.apache.wicket.Session;
import org.apache.wicket.security.WaspSession;
import org.apache.wicket.security.hive.authentication.LoginContext;
import org.apache.wicket.security.hive.authentication.Subject;
import org.apache.wicket.security.authentication.LoginException;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;

public class SpringSecurityLoginContext extends LoginContext
{
	private final static Logger logger = Logger.getLogger(SpringSecurityLoginContext.class);
	
	private AbstractAuthenticationToken token;

	/**
	 * 
	 * Constructor for logoff purposes.
	 */
	public SpringSecurityLoginContext()
	{
		((WaspSession) Session.get()).invalidateNow();
		
		SecurityContextHolder.clearContext();
	}

	/**
	 * Constructs a new LoginContext with the provided Spring Security AuthenticationToken.
	 * 
	 * @param token contains credentials like username and password
	 */
	public SpringSecurityLoginContext(AbstractAuthenticationToken token)
	{
		this.token = token;
	}

	@Override
	public Subject login() throws LoginException
	{
		if (token == null)
		{
			throw new LoginException("Insufficient information to login");
		}
		try
		{
			AuthenticationManager authenticationManager = ((AcantoPlatformApplication)Application.get()).getAuthenticationManager();

            if (authenticationManager == null)
            {            	
            	throw new LoginException(
            			"AuthenticationManager is not available, check if your Spring config contains a property for the authenticationManager in your wicketApplication bean.");
            }
            
            Authentication authResult = authenticationManager.authenticate(token);
            
            setAuthentication(authResult);
        }
		catch (LockedException statusInvalid)
		{
			logger.info("Failed login for user '" + token.getPrincipal() 
					+ "'. Caused by a particular user account status (locked, disabled etc). ");
			
			setAuthentication(null);
			
			throw new LoginException("Failed login for user : account status (locked, disabled, etc)");
		}
		catch (BadCredentialsException e)
		{
			logger.info("Failed login by user '" + token.getPrincipal()
					+ "'. Because the credentials are invalid. For this exception to be thrown, " 
					+ " it means the account is neither locked nor disabled");
			
			setAuthentication(null);
			
			throw new LoginException("Bad credentials");
		}
        catch (AuthenticationException e)
		{
			logger.error("Could not authenticate a user", e);
			setAuthentication(null);

			throw new LoginException("Could not authenticate a user");
		}
		catch (RuntimeException e)
		{
			logger.error("Unexpected exception while authenticating a user", e);
			setAuthentication(null);

			throw new LoginException("Unexpected exception while authenticating a user");
		}

        // cleanup
        token = null;
        
        // return result
        return new SpringSecuritySubject();
	}
	
    /**
     * Sets the Spring Security authentication.
     */
    private void setAuthentication(Authentication authentication)
    {
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    /**
     * Notify Spring Security.
     */
    public void notifyLogoff(Subject subject)
    {
        setAuthentication(null);
    }

}
